/*
 * Copyright (c) 2009, Hamish Morgan. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the University of Sussex nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package locusts.common.entities;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.swing.event.EventListenerList;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;

/**
 *
 * @author Hamish Morgan
 */
public class EntityList extends CopyOnWriteArrayList<Entity> implements
        Serializable, Cloneable {

    protected EntityList(List<Entity> entities) {
        super(entities);
    }

    public EntityList() {
        super();
    }

    private EntityList(EntityList that) {
        super(that);
    }

    @Override
    public boolean add(Entity e) {
        boolean result = super.add(e);
        if (result)
            fireIntervalAdded(size() - 1, size() - 1);
        return result;
    }

    @Override
    public void add(int index, Entity element) {
        super.add(index, element);
        fireIntervalAdded(index, index);
    }

    @Override
    public boolean addAll(Collection<? extends Entity> c) {
        boolean result = super.addAll(c);
        if (result)
            fireIntervalAdded(size() - c.size(), size() - 1);
        return result;
    }

    @Override
    public boolean addAll(int index,
            Collection<? extends Entity> c) {
        boolean result = super.addAll(index, c);
        if (result)
            fireIntervalAdded(index, index + c.size() - 1);
        return result;
    }

    @Override
    public int addAllAbsent(Collection<? extends Entity> c) {
        int result = super.addAllAbsent(c);
        if (result > 0)
            fireIntervalAdded(size() - result, size() - 1);
        return result;
    }

    @Override
    public boolean addIfAbsent(Entity e) {
        boolean result = super.addIfAbsent(e);
        if (result)
            fireIntervalAdded(size() - 1, size() - 1);
        return result;
    }
//
//    @Override
//    public void clear() {
//        int size = size();
//        if (size > 0) {
//            super.clear();
//            fireIntervalRemoved(0, size - 1);
//        }
//    }
//
//    @Override
//    public boolean remove(Object o) {
//        int i = super.indexOf(o);
//        if (i != -1) {
//            remove(i);
//            return true;
//        } else {
//            return false;
//        }
//    }
//
//    @Override
//    public Entity remove(int index) {
//        Entity result = super.remove(index);
//        System.out.println(">>>> fireGameEntitiesRemovedEvent()");
//        fireIntervalRemoved(index, index);
//        System.out.println("<<<< fireGameEntitiesRemovedEvent()");
//        return result;
//    }
//
//    @Override
//    public boolean removeAll(Collection<?> c) {
//        int size = size();
//        boolean result = super.removeAll(c);
//        if (result)
//            fireContentsChanged(0, size - 1);
//        return result;
//    }
//
//    @Override
//    public boolean retainAll(Collection<?> c) {
//        int size = size();
//        boolean result = super.retainAll(c);
//        if (result)
//            fireContentsChanged(0, size - 1);
//        return result;
//    }
//
//    @Override
//    public Entity set(int index, Entity element) {
//        Entity result = super.set(index, element);
//        fireContentsChanged(index, index);
//        return result;
//    }

    public List<Entity> getDeadEntities() {
        List<Entity> dead = new ArrayList<Entity>();
        for (Entity e : this)
            if (!e.isAlive())
                dead.add(e);
        return dead;
    }

    public List<Entity> getType(int typeId) {
        List<Entity> result = new ArrayList<Entity>();
        for (Entity e : this) {
            if (e.getTypeId() == typeId)
                result.add(e);
        }
        return result;
    }

    public List<Entity> getAllByType(EntityType type) {
        return getType(type.getId());
    }

    public void moveToFront(Entity e) {
        int zmax = -Integer.MAX_VALUE;
        for (Entity ent : this)
            if (!ent.equals(e) && ent.getzOrder() > zmax)
                zmax = ent.getzOrder();
        if (e.getzOrder() != zmax + 1) {
            e.setzOrder(zmax + 1);
            e.setModified();
        }
    }

    public void moveToFront(EntityType type) {
        int zmax = -Integer.MAX_VALUE;
        for (Entity ent : this)
            if (ent.getTypeId() != type.getId() && ent.getzOrder() > zmax)
                zmax = ent.getzOrder();
        for (Entity ent : this.getType(type.getId())) {
            if (ent.getzOrder() != zmax + 1) {
                ent.setzOrder(zmax + 1);
                ent.setModified();
            }
        }
    }

    public void moveToBack(Entity e) {
        int zmin = Integer.MAX_VALUE;
        for (Entity ent : this)
            if (!ent.equals(e) && ent.getzOrder() < zmin)
                zmin = ent.getzOrder();
        if (e.getzOrder() != zmin - 1) {
            e.setzOrder(zmin - 1);
            e.setModified();
        }
    }

    public void moveToBack(EntityType type) {
        int zmin = Integer.MAX_VALUE;
        for (Entity ent : this)
            if (ent.getTypeId() != type.getId() && ent.getzOrder() < zmin)
                zmin = ent.getzOrder();
        for (Entity ent : this.getType(type.getId())) {
            if (ent.getzOrder() != zmin - 1) {
                ent.setzOrder(zmin - 1);
                ent.setModified();
            }
        }
    }

    public <T extends Entity> List<T> getType(int typeId, Class<T> c) {
        List<T> result = new ArrayList<T>();
        for (Entity e : this) {
            if (c.isInstance(e) && e.getTypeId() == typeId)
                result.add(c.cast(e));
        }
        return result;
    }

    public <T extends Entity> List<T> getAllByType(EntityType type, Class<T> c) {
        return getType(type.getId(), c);
    }

    /**
     * 
     * @param id unique id of the entity to find
     * @return the entity or null if it wasn't found.
     */
    public Entity getById(int id) {
        for (Entity e : this)
            if (e.getId() == id)
                return e;
        return null;
    }

    @Override
    public EntityList clone() {
        return new EntityList(this);
    }
    // ==============================================
    // List model stuff
    //
    private final transient EventListenerList listeners =
            new EventListenerList();

    protected void fireIntervalAdded(final int index0, final int index1) {
        final ListDataEvent e = new ListDataEvent(
                this, ListDataEvent.INTERVAL_ADDED, index0, index1);
        for (ListDataListener l : listeners.getListeners(
                ListDataListener.class))
            l.intervalAdded(e);
    }

    protected void fireIntervalRemoved(int index0, int index1) {
        final ListDataEvent e = new ListDataEvent(this,
                ListDataEvent.INTERVAL_REMOVED, index0, index1);
        for (ListDataListener l : listeners.getListeners(
                ListDataListener.class))
            l.intervalRemoved(e);
    }

    protected void fireContentsChanged(int index0, int index1) {
        final ListDataEvent e = new ListDataEvent(this,
                ListDataEvent.CONTENTS_CHANGED, index0, index1);
        for (ListDataListener l : listeners.getListeners(
                ListDataListener.class))
            l.contentsChanged(e);
    }

    public void addListDataListener(ListDataListener listener) {
        listeners.add(ListDataListener.class, listener);
    }

    public void removeListDataListener(ListDataListener listener) {
        listeners.remove(ListDataListener.class, listener);
    }
}
